package com.piece.module.file.util.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.piece.core.framework.exception.ErrorCode;
import com.piece.core.framework.exception.FrameWorkException;
import com.piece.core.framework.util.collection.CollectionUtil;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.module.file.util.convert.CustomStringConverter;
import org.apache.poi.ss.usermodel.BorderStyle;
import java.io.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Excel读写工具类
 */
public class ExcelUtil {

    public static <T> List<T> read(InputStream is, ExcelParam param) {
        List<T> resultList = new ArrayList<>();
        Function<List<T>, Void> function = data -> {
            resultList.addAll(data);
            return null;
        };

        read(is, param, function);
        return resultList;
    }

    /**
     * 获取Excel中数据
     */
    public static <T> void read(InputStream is, ExcelParam param, Function function) {
        ReadListener readListener;
        Class convertType = param.getConvertType();
        if (null == convertType || Map.class.isAssignableFrom(convertType)) {
            param.setConvertType(null);
            readListener = new ExcelReadMapListener(param, function);
        } else {
            readListener = new ExcelReadListener<T>(param, function);
        }
        read(is, param, readListener);
    }

    /**
     * 获取Excel中数据
     */
    public static <T> void read(InputStream is, ExcelParam param, ReadListener readListener) {
        if (StringUtil.isNotEmpty(param.getSheetName())) {
            EasyExcel.read(is, param.getConvertType(), readListener)
                    .sheet(param.getSheetName()).registerConverter(new CustomStringConverter()).doRead();
        } else {
            EasyExcel.read(is, param.getConvertType(), readListener)
                    .sheet().registerConverter(new CustomStringConverter()).doRead();
        }
    }

    /**
     * 生成Excel文件流
     */
    public static InputStream write(ExcelParam param) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        write(os, param);
        return new ByteArrayInputStream(os.toByteArray());
    }

    /**
     * 生成Excel文件流
     */
    public static void write(OutputStream os, ExcelParam param) {
        try {
            param = convertDataType(param);
            ExcelTypeEnum excelType = param.getExcelType();
            String sheetName = param.getSheetName();
            List<List<String>> headList = param.getHeadList();

            if (CollectionUtil.isNotEmpty(headList)) {
                EasyExcel.write(os)
                        .head(headList)
                        .excelType(excelType)
                        .sheet(sheetName)
                        .registerConverter(new CustomStringConverter())
                        .registerWriteHandler(new MatchExcelWidthStyleStrategy())
                        .registerWriteHandler(defaultCellStyleStrategy())
                        .registerWriteHandler(new ExcelWriterHandler(param))
                        .doWrite(param.getData());
            } else {
                Class convertType = param.getConvertType();
                EasyExcel.write(os, convertType)
                        .excelType(excelType)
                        .sheet(sheetName)
                        .registerConverter(new CustomStringConverter())
                        .registerWriteHandler(new MatchExcelWidthStyleStrategy())
                        .registerWriteHandler(new ExcelWriterHandler(param))
                        .doWrite(param.getData());
            }

            os.flush();
        } catch (Exception e) {
            e.printStackTrace();
            throw new FrameWorkException(ErrorCode.EXPORT);
        }
    }

    private static <T> ExcelParam convertDataType(ExcelParam param) {
        // 提取转换类型
        final boolean needConvert = param.isConvert();
        if (needConvert) {
            final Class<T> convertType = param.getConvertType();
            List<?> originDataList = param.getData();
            if (CollectionUtil.isNotEmpty(originDataList)) {
                List convertDataList = originDataList.stream().map(row -> {
                    if (row instanceof Map) {
                        List<List<String>> headList = param.getHeadList();
                        List<Object> result = new ArrayList<>();
                        headList.stream().forEach(title -> {
                            result.add(((Map) row).get(title.get(0)));
                        });
                        return result;
                    } else {
                        T target = BeanUtil.newInstance(convertType);
                        try {
                            BeanUtil.copyProperties(row, target);
                        } catch (Exception e) {
                        }
                        return target;
                    }
                }).collect(Collectors.toList());

                param.setData(convertDataList);
            }
        }

        return param;
    }

    private static HorizontalCellStyleStrategy defaultCellStyleStrategy() {
        // 定义头部样式策略
        WriteCellStyle headCellStyle = new WriteCellStyle();
        headCellStyle.setBorderBottom(BorderStyle.MEDIUM);
        WriteFont headFont = new WriteFont();
        headFont.setFontHeightInPoints((short) 14);
        headCellStyle.setWriteFont(headFont);
        // 内容样式策略
        WriteCellStyle contentCellStyle = new WriteCellStyle();
        // 自动换行
        contentCellStyle.setWrapped(true);
        WriteFont contentFont = new WriteFont();
        headFont.setFontHeightInPoints((short) 12);
        contentCellStyle.setWriteFont(contentFont);

        return new HorizontalCellStyleStrategy(headCellStyle, contentCellStyle);
    }

    /**
     * 填充模板数据
     */
    public static void fillTemplate(OutputStream os, ExcelParam param) {
        try {
            param = convertDataType(param);

            ExcelWriter writer = EasyExcel.write(os).withTemplate(param.getTemplateFile()).build();
            WriteSheet sheet = EasyExcel.writerSheet(param.getSheetName()).build();
            // 添加换行
            FillConfig config = FillConfig.builder().forceNewRow(param.isForceNewRow()).build();
            // 填充列表数据
            if (CollectionUtil.isNotEmpty(param.getData())) {
                writer.fill(param.getData(), config, sheet);
            }
            // 填充模板数据
            if (null != param.getFillData()) {
                writer.fill(param.getFillData(), sheet);
            }

            writer.finish();
        } catch (Exception e) {
            throw new FrameWorkException(ErrorCode.EXPORT);
        }
    }
}
